﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

/**
 * An extension of the Camera class, with functionality for either
 * following a given entity (Player, probably), or moving to a given
 * point.
 */
public class FollowerCamera : Camera
{
    // Member vars here.
    private CollidableObject m_target;
    private Vector3 m_targetPosition;
    private double m_moveVelocity;
    private State m_state;

    private bool m_isShaking;
    private double m_shakeTimer;
    private float m_yVelocity;
    private bool m_yUpward;
    private float m_constVelocity;
    private float m_maxVelocity;

    /**
     * Basic FSM states.
     */
    enum State
    {
        kState_INVALID = -1,
        kState_Idle,
        kState_Target,
        kState_MoveTo
    }

    /**
     * Our constructor. Du-dun.
     */
    public FollowerCamera(Vector3 position, CollidableObject target)
        : base(position)
    {
        m_isShaking = false;
        setTarget(target);
    }

    /**
     * Move to the given location at the given speed.
     * Because of the way setTarget works, we should
     * call setTarget immediately before moveTo if we want
     * to affect the behavior once the Camera reaches the target
     */
    public void moveTo(Vector3 position, double velocity)
    {
        m_targetPosition = position;
        m_moveVelocity = velocity;
        m_state = State.kState_MoveTo;
    }

    /**
     * Set our lookAt target.
     * If null, then we won't move unless given a moveTo command
     */
    public void setTarget(CollidableObject target)
    {
        m_target = target;

        if (m_target != null)
        {
            m_state = State.kState_Target;
            s_position = m_target.position - new Vector3(Game.kScreenWidth / 2, Game.kScreenHeight, m_target.position.Z);
        }
        else
        {
            m_state = State.kState_Idle;
        }
    }

    /**
     * Tell the Camera to shake on the y-axis for a given time
     */
    public void shakeFor(float distance, double millis)
    {
        m_isShaking = true;
        m_shakeTimer = millis;
        m_yVelocity = 0.0f;
        m_yUpward = true;

        m_constVelocity = 2000.0f/distance;
        if (m_constVelocity > 30.0f)
        {
            m_constVelocity = 30.0f;
        }
        m_maxVelocity = m_constVelocity * 4;

//        Game.getGame().getConsole().print("Steve foot: " + m_constVelocity);
    }

    /**
     * Our update function.
     * Determiens functionality based on our current state.
     */
    override public void update(double dt)
    {
        switch(m_state)
        {
            case State.kState_Idle:
                // Idle around.
                break;
            case State.kState_Target:
                /*
                 * Rather than moving _to_ the target, we'll assume that the target is always
                 * moving slow enough that simply snapping to his position will look fine.
                 */
                if (m_target != null)
                {
                    s_position = m_target.position - new Vector3(Game.kScreenWidth/2, Game.kScreenHeight, m_target.position.Z);
                    s_position.Y = s_position.Y - m_target.position.Z/3.0f;
                }
                else
                {
                    m_state = State.kState_Idle;
                }
                break;
            case State.kState_MoveTo:
                s_position = m_targetPosition - new Vector3(Game.kScreenWidth / 2, Game.kScreenHeight, m_target.position.Z);
                s_position.Y = s_position.Y - m_targetPosition.Z / 3;
                m_state = State.kState_Idle;
                /*
                 * Do one of three things:
                 * 1. If we are far enough way, move toward the targetPosition at the given velocity.
                 * 2. If we are close enough that a move of velocity-amount would overshoot the target,
                 *    then simply teleport to the targetPosition.
                 * 3. If we are at the targetPosition, then switch states based on whether we have a
                 *    CO target or not.
                 */
                // TODO: Implement, yo.
                break;
        }

        // Shake camera if needed
        if (m_isShaking)
        {
            GamePad.SetVibration(PlayerIndex.One, 0.5f, 0.5f);

            // Decrement timer to shake for given time
            m_shakeTimer -= dt;
            if (m_shakeTimer <= 0.0)
            {
                m_isShaking = false;
            }

            s_position.Y += m_yVelocity;

            m_yVelocity += m_yUpward ? m_constVelocity : -m_constVelocity;

            if (m_yVelocity >= m_maxVelocity)
            {
                m_yUpward = false;
            }
            else if (m_yVelocity <= -m_maxVelocity)
            {
                m_yUpward = true;
            }
        }
        else
        {
            GamePad.SetVibration(PlayerIndex.One, 0.0f, 0.0f);
        }

        

        base.update(dt);
    }
}
